package XMLProcessing;

import Exceptions.ColumnNotFoundException;
import Exceptions.NullPrimaryKeyException;
import Exceptions.TableExistsException;
import Exceptions.TableNotFoundException;
import command.*;
import java.util.*;

import org.xml.sax.SAXException;
import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
import org.w3c.dom.Element;

public class TableProcessing {

	public static Map<Integer, List<String>> retrieveValues(String xmlFile,
			Condition t) {
		List<String> columns = getColumns(xmlFile);
		return retrieveValues(xmlFile, columns, t);
	}

	public static Map<Integer, List<String>> retrieveValues(String xmlFile,
			List<String> fields, ConditionNode t) throws ColumnNotFoundException {
		Map<Integer, List<String>> result = new HashMap<Integer, List<String>>();
		List<String> columns = getColumns(xmlFile);

		for (String f : fields) {
			if (!columns.contains(f))
				throw new ColumnNotFoundException(f);
		}

		try {
			File fXmlFile = new File(xmlFile);
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder dBuilder;
			dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(fXmlFile);
			doc.getDocumentElement().normalize();

			NodeList nList = doc.getElementsByTagName("Row");

			Integer row = new Integer(0);
			for (int temp = 0; temp < nList.getLength(); temp++) {

				Node nNode = nList.item(temp);
				if (nNode.getNodeType() == Node.ELEMENT_NODE) {
					Element eElement = (Element) nNode;
				
					if (t == null
							|| t.eval(getRow(eElement,getColumns(xmlFile)))) {

						List<String> data = new ArrayList<String>();
						for (String s : fields) {
							data.add(getTagValue(s, eElement));
						}
						result.put(row, data);
						row = row + 1;
					}
				}
			}
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}



	public static Boolean insertValues(String xmlFile,
			HashMap<String, String> hash) throws NullPrimaryKeyException {
		try {
			DocumentBuilderFactory docFactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder docBuilder;

			docBuilder = docFactory.newDocumentBuilder();

			Document doc = docBuilder.parse(xmlFile);

			List<String> columns = getColumns(xmlFile);

			for (Map.Entry<String, String> entry : hash.entrySet()) {
				if (!columns.contains(entry.getKey()))
					throw new ColumnNotFoundException(entry.getKey());
			}

			// get the primary key
			NodeList structureList = doc.getElementsByTagName("Structure");
			Element keyElement = (Element) structureList.item(0);
			String keyCol = getTagValue("Key", keyElement);

			// can't insert if the primary key is missing
			if (!hash.containsKey(keyCol)) {
				throw new NullPrimaryKeyException();
			}

			String s = (String) hash.get(keyCol);

			Node refChild = null;

			NodeList rowList = doc.getElementsByTagName("Row");
			for (int temp = 0; temp < rowList.getLength(); temp++) {
				Node nNode = rowList.item(temp);
				if (nNode.getNodeType() == Node.ELEMENT_NODE) {
					Element eElement = (Element) nNode;

					String keyValue = getTagValue(keyCol, eElement);
					System.out.println(keyValue);
					if (keyValue.compareTo(s) > 0) {
						refChild = nNode;
						System.out.println("a trecut" + keyValue);
						temp = rowList.getLength();
					}
				}
			}

			// the new row record
			Element row = doc.createElement("Row");
			for (String col : columns) {
				Element el = doc.createElement(col);
				if (hash.containsKey(col))
					el.appendChild(doc.createTextNode(hash.get(col)));
				else
					el.appendChild(doc.createTextNode(" "));
				row.appendChild(el);
			}

			// insert row into xml file
			NodeList dataList = doc.getElementsByTagName("Data");
			Node dataNode = dataList.item(0);

			if (refChild != null)
				dataNode.insertBefore(row, refChild);
			else
				dataNode.appendChild(row);

			TransformerFactory transformerFactory = TransformerFactory
					.newInstance();
			Transformer transformer = transformerFactory.newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			DOMSource source = new DOMSource(doc);
			StreamResult result = new StreamResult(new File(xmlFile));
			transformer.transform(source, result);
			return true;
		} catch (ParserConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		} catch (TransformerConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		} catch (TransformerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		} catch (SAXException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return false;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return false;

	}

	private static String getTagValue(String sTag, Element eElement)
			throws ColumnNotFoundException {

		try {
			NodeList nlList = eElement.getElementsByTagName(sTag).item(0)
					.getChildNodes();
			Node nValue = (Node) nlList.item(0);
			if (nValue == null)
				throw new ColumnNotFoundException(sTag);
			return nValue.getNodeValue();
		} catch (NullPointerException e) {
			throw new ColumnNotFoundException(sTag);
		}
	}
	
	private static HashMap<String,String> getRow(Element eElement, List<String> columns) {
		 HashMap<String,String> map = new  HashMap<String,String>();
		for(String t : columns) {
			map.put(t,getTagValue(t,eElement));
		}
		return map;
	}
	
	public static List<String> getColumns(String xmlFile) {

		List<String> columns = new ArrayList<String>();

		try {
			File fXmlFile = new File(xmlFile);
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder dBuilder;
			dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(fXmlFile);
			doc.getDocumentElement().normalize();

			Node columnNode = doc.getElementsByTagName("Columns").item(0);

			NodeList columnsList = columnNode.getChildNodes();

			for (int tempStr = 0; tempStr < columnsList.getLength(); tempStr++) {

				Node nNode = columnsList.item(tempStr);

				if (nNode.getNodeType() == Node.ELEMENT_NODE) {
					columns.add(nNode.getNodeName());
				}
			}

		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return columns;
	}

	public static Boolean createTable(String tableName,
			ArrayList<String> fields, String primaryKey)
			throws TableExistsException {
		try {
		if (DatabaseProcessing.retrieveTable(tableName) != null) {
			
			throw new TableExistsException(tableName);
		}
		}
		catch (TableNotFoundException e){
			
		}
		
		try {
			String file = DatabaseProcessing.addTable(tableName);

			DocumentBuilderFactory docFactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
			
			Document doc = docBuilder.newDocument();
			Element rootElement = doc.createElement("Table");
			doc.appendChild(rootElement);
				
			Element structure = doc.createElement("Structure");
			rootElement.appendChild(structure);

			Element key = doc.createElement("Key");
			key.appendChild(doc.createTextNode(primaryKey));
			structure.appendChild(key);

			Element data = doc.createElement("Data");
			rootElement.appendChild(data);
			Element columns = doc.createElement("Columns");
			for (String t : fields) {
				Element col = doc.createElement(t);
				columns.appendChild(col);
			}
			structure.appendChild(columns);
			// write the content into xml file
			TransformerFactory transformerFactory = TransformerFactory
					.newInstance();
			Transformer transformer = transformerFactory.newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			DOMSource source = new DOMSource(doc);
			StreamResult result = new StreamResult(new File(file));
			transformer.transform(source, result);
			
		} catch (ParserConfigurationException pce) {
			pce.printStackTrace();
		} catch (TransformerConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TransformerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return true;
	}

	public static Boolean dropTable(String tableName)
			throws TableNotFoundException {
		String t = DatabaseProcessing.retrieveTable(tableName);

		File f = new File(t);
		f.delete();
		DatabaseProcessing.removeTable(tableName);
		return true;
	}

	public static Integer updateTable(String xmlFile,
			Map<String, String> values, ConditionNode t) {
		List<String> columns = getColumns(xmlFile);
		Integer row = new Integer(0);
		try {
			File fXmlFile = new File(xmlFile);
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder dBuilder;
			dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(fXmlFile);
			doc.getDocumentElement().normalize();
			NodeList nList = doc.getElementsByTagName("Row");

			for (int temp = 0; temp < nList.getLength(); temp++) {

				Node nNode = nList.item(temp);
				if (nNode.getNodeType() == Node.ELEMENT_NODE) {
					Element eElement = (Element) nNode;

					if (t == null
							|| t.eval(getRow(eElement,getColumns(xmlFile)))) {
						for (String key : values.keySet()) {
							setTagValue(eElement, key, values.get(key), doc);
						}
						row = row + 1;
					}

				}
			}
			TransformerFactory transformerFactory = TransformerFactory
					.newInstance();
			Transformer transformer = transformerFactory.newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			DOMSource source = new DOMSource(doc);
			StreamResult result = new StreamResult(new File(xmlFile));
			transformer.transform(source, result);
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TransformerConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TransformerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return row;
	}

	private static void setTagValue(Element eElement, String key,
			String string, Document doc) {
		NodeList nlList = eElement.getElementsByTagName(key).item(0)
				.getChildNodes();
		Node nValue = (Node) nlList.item(0);
		if (string.equals(""))
			string = " ";
		nValue.setNodeValue(string);
	}

	public static Integer deleteTable(String xmlFile, ConditionNode t) {
		List<String> columns = getColumns(xmlFile);
		Integer row = new Integer(0);
		try {
			File fXmlFile = new File(xmlFile);
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory
					.newInstance();
			DocumentBuilder dBuilder;
			dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(fXmlFile);
			doc.getDocumentElement().normalize();
			NodeList nList = doc.getElementsByTagName("Row");

			for (int temp = 0; temp < nList.getLength(); temp++) {

				Node nNode = nList.item(temp);
				System.out.println(nNode);
				if (nNode.getNodeType() == Node.ELEMENT_NODE) {
					Element eElement = (Element) nNode;
					if (t == null
							|| t.eval(getRow(eElement,getColumns(xmlFile)))) {
						nNode.getParentNode().removeChild(nNode);
						temp--;
						row = row + 1;
					}
				}
			}
			TransformerFactory transformerFactory = TransformerFactory
					.newInstance();
			Transformer transformer = transformerFactory.newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			DOMSource source = new DOMSource(doc);
			StreamResult result = new StreamResult(new File(xmlFile));
			transformer.transform(source, result);
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (TransformerConfigurationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TransformerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return row;

	}

}
